package editor.widgets;

import editor.PwnIDE;
import model.caret.Caret;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * This is used for displaying links to locations in a project, for example, locations of compile errors,
 * locations of references to a given symbol, or locations of TODOs.
 *
 * NOTE: I can't think of any reason that it would make sense to support HeaderCarets as locations, but it's not hard to support, so it should be supported.
 *
 * @author Neil Dickson
 */
public class ReferencePanel extends JComponent implements MouseListener {
	private	int[]		levels;
	private	String[]	texts;
	private	Caret[]		carets;
	private	ImageIcon[]	icons;
	private JTree		tree;
	private	PwnIDE		editor;

	private	HashMap<DefaultMutableTreeNode,Integer>	nodeToIndexMap;

	public static final Color	BACKGROUND_COLOUR	= new Color(0,0,0,255);
	public static final Color	SELECTION_COLOUR	= new Color(0,40,90);

	private static final DefaultMutableTreeNode EMPTY_TREE_NODE = new DefaultMutableTreeNode();

	/**
	 * Creates a ReferencePanel with the specified references optionally put into multiple categories.
	 * @param editor the main PwnIDE window
	 * @param levels names of the categories, or null if none
	 * @param texts arrays of {@link Caret}s by category, or only one array if no categories
	 * @param carets arrays of Strings by category, or only one array if no categories
	 * @param icons arrays of {@link ImageIcon}s by category, or only one if no categories
	 */
	public ReferencePanel(PwnIDE editor,int[] levels,String[] texts,Caret[] carets,ImageIcon[] icons) {
		this.levels = levels;
		this.texts = texts;
		this.carets = carets;
		this.icons = icons;
		this.editor = editor;
		this.nodeToIndexMap = new HashMap<DefaultMutableTreeNode,Integer>(texts.length);
		setLayout(new FlowLayout(FlowLayout.LEFT));
		updateTree();
		tree.addComponentListener(new ComponentAdapter(){
			public void componentResized(ComponentEvent e) {
				setSize(tree.getX()+tree.getWidth(),tree.getY()+tree.getHeight());
			}
		});
	}

	private void updateTree() {
		if (tree!=null) {
			remove(tree);
		}
		DefaultMutableTreeNode root = getTree();
		tree = new JTree((root!=null) ? root : EMPTY_TREE_NODE);
		for (int i=0;i<tree.getRowCount();++i) {
			tree.expandRow(i);
		}
		tree.setCellRenderer(new TreeRenderer());
		tree.setRootVisible(true);
		tree.setEditable(false);
		tree.setDragEnabled(false);
		tree.addMouseListener(this);
		add(tree);
		invalidate();
		validate();
	}

	public void paintComponent(Graphics g) {
		g.setColor(getBackground());
		g.fillRect(0,0,getWidth(),getHeight());
	}

	public Dimension getMinimumSize() {
		return new Dimension(tree.getWidth()+tree.getX(),tree.getHeight()+tree.getY());
	}

	private DefaultMutableTreeNode getTree() {
		if (levels.length==0 || levels[0]!=0) {
			return null;
		}
		int maxLevel = 0;
		for (int i=1;i<levels.length;++i) {
			if (levels[i]<=0 || (levels[i]-levels[i-1]>1)) {
				return null;
			}
			maxLevel = Math.max(maxLevel,levels[i]);
		}
		ArrayList<DefaultMutableTreeNode> lastOnLevel = new ArrayList<DefaultMutableTreeNode>(maxLevel);
		DefaultMutableTreeNode root = new DefaultMutableTreeNode(texts[0]);
		nodeToIndexMap.put(root,0);
		lastOnLevel.add(root);
		for (int i=1;i<levels.length;++i) {
			DefaultMutableTreeNode node = new DefaultMutableTreeNode(texts[i]);
			nodeToIndexMap.put(node,i);
			lastOnLevel.get(levels[i]-1).add(node);
			while (lastOnLevel.size()>levels[i]) {
				lastOnLevel.remove(lastOnLevel.size()-1);
			}
			lastOnLevel.add(node);
		}
		lastOnLevel.clear();
		return root;
	}

	public void mouseClicked(MouseEvent e) {
		if (e.getButton()==MouseEvent.BUTTON1) {
			if (e.getClickCount()==2) {
				TreePath path = tree.getPathForLocation(e.getX(),e.getY());
				if (path!=null) {
					DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
					Integer index = nodeToIndexMap.get(node);
					if (index!=null && index>=0 && index<carets.length && carets[index]!=null) {
						PwnIDE.ui.goToCaret(carets[index]);
					}
				}
			}
		}
	}
	public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}

	private class TreeRenderer extends DefaultTreeCellRenderer {
		public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
			TreePath path = tree.getPathForRow(row);
			if (path!=null) {
				DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
				Integer index = nodeToIndexMap.get(node);
				if (index!=null && index>=0 && index<carets.length) {
					setText(texts[index]);
					setIcon(icons[index]);
				}
			}
			if (selected) {
				setBackground(SELECTION_COLOUR);
			}
			else {
				setBackground(BACKGROUND_COLOUR);
			}
			return this;
		}
	}
}
